package fussbudget.gui;

import interfaces.IpaymentEntityListable;

import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;

import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;
import org.jdesktop.swingx.treetable.TreeTableNode;

import fussbudget.Address;
import fussbudget.PaymentEntity;
import fussbudget.PaymentEntity.Type;
import fussbudget.SQL.FBSQL;
import fussbudget.SQL.SQLUtility;
import fussbudget.abstracts.AbstractDraggableElement;
import fussbudget.abstracts.AbstractTreeTableWithPopup;
import fussbudget.gui.components.BasicPopupMenu;
import fussbudget.gui.components.HasPopupMenu;

public class PaymentEntityTablePanel extends JScrollPane implements HasPopupMenu {
	private static final boolean								IS_GROUP	= true;
	private static final boolean								NO_SQL		= true;

	private LinkedHashMap<Integer, AbstractDraggableElement>	groupIDs	= new LinkedHashMap<Integer, AbstractDraggableElement>();

	public Container											parent;

	public AbstractTreeTableWithPopup							table;																	// = new AbstractTreeTableWithPopup();
	public PaymentEntityTableModel								model;																	// = new PaymentEntityTableModel(root);
	public JScrollPane											scroll;
	public BasicPopupMenu										popup;
	public boolean												isVerbose	= true;

	private PaymentEntity.Type									type;

	public PaymentEntityTablePanel(String name, Container parent, PaymentEntity.Type type) {
		super();

		this.parent = parent;
		this.type = type;

		SQLUtility.lock();
		PaymentEntity root = new PaymentEntity("root", EnumSet.of(PaymentEntity.Type.ROOT));
		SQLUtility.unlock();

		model = new PaymentEntityTableModel(root);
		switch (type) {
			case PAYER:
				populateModel(EnumSet.of(PaymentEntity.Type.PAYER, PaymentEntity.Type.GROUP));
				populateModel(EnumSet.of(PaymentEntity.Type.PAYER));
				break;
			case PAYEE:
				populateModel(EnumSet.of(PaymentEntity.Type.PAYEE, PaymentEntity.Type.GROUP));
				populateModel(EnumSet.of(PaymentEntity.Type.PAYEE));
				break;
		}

		setBorder(new TitledBorder(name));
		table = new AbstractTreeTableWithPopup(model);
		table.setFillsViewportHeight(true);
		table.setDefaultRenderer(Object.class, new PaymentEntityTableRenderer());
		table.setTreeCellRenderer(new PaymentEntityTreeRenderer());

		this.getViewport().setView(table);

		setupPopup();
	}

	public void populateModel(EnumSet<Type> types) {
		ResultSet rs = null;
		if (types.equals(EnumSet.of(Type.PAYER, Type.GROUP))) {
			groupIDs = new LinkedHashMap<Integer, AbstractDraggableElement>();
			rs = FBSQL.getResultSetFromActiveDB("paymentEntity", "*", "isPayer=true && isGroup=true");
		} else if (types.equals(EnumSet.of(Type.PAYEE, Type.GROUP))) {
			groupIDs = new LinkedHashMap<Integer, AbstractDraggableElement>();
			rs = FBSQL.getResultSetFromActiveDB("paymentEntity", "*", "isPayee=true && isGroup=true");
		} else if (types.equals(EnumSet.of(Type.PAYER))) {
			rs = FBSQL.getResultSetFromActiveDB("paymentEntity", "*", "isPayer=true && isGroup=false");
		} else if (types.equals(EnumSet.of(Type.PAYEE))) {
			rs = FBSQL.getResultSetFromActiveDB("paymentEntity", "*", "isPayee=true && isGroup=false");
		}

		SQLUtility.lock();
		if (rs != null) {
			try {
				ArrayList<PaymentEntity> tempList = new ArrayList<PaymentEntity>();
				while (rs.next()) {
					PaymentEntity newEntity = new PaymentEntity(rs);
					if (newEntity.isGroup()) {
						groupIDs.put(newEntity.getId(), newEntity);
					}
					tempList.add((PaymentEntity) newEntity);
				}

				for (PaymentEntity currEntity : tempList) {
					PaymentEntity group;
					if (currEntity.getParentElement().getId() == -1) {
						group = (PaymentEntity) model.getRoot();
					} else {
						group = (PaymentEntity) groupIDs.get(currEntity.getParentElement().getId());
					}
					model.insertNodeInto(currEntity, group, group.getChildCount());
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		SQLUtility.unlock();
	}

	public void setupPopup() {
		ArrayList<HasPopupMenu> haveMenu = new ArrayList<HasPopupMenu>();
		haveMenu.add(this);
		haveMenu.add(table);

		popup = new BasicPopupMenu(haveMenu);
		popup.addMenuItem("Add", new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				System.out.println("Add new PaymentEntity...");
				int row = popup.getRow();
				IpaymentEntityListable currElement = null;
				PaymentEntity newEntity = new PaymentEntity("New", EnumSet.of(type));
				if (row != -1) {
					TreePath path = table.getPathForRow(row);
					PaymentEntity leaf = (PaymentEntity) path.getLastPathComponent();

					if (leaf.isGroup()) {
						model.insertNodeInto(newEntity, leaf, leaf.getChildCount());
					} else {
						model.insertNodeInto(newEntity, leaf.getParentElement(), leaf.getParentElement().getChildCount());
					}
				} else {
					model.insertNodeInto(newEntity, table.getRoot(), table.getRoot().getChildCount());
				}
			}
		});
		popup.addMenuItem("Add Group", new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				System.out.println("Add new Group...");
				PaymentEntity newGroup = new PaymentEntity("New Group", EnumSet.of(type, PaymentEntity.Type.GROUP));
				model.insertNodeInto(newGroup, table.getRoot(), table.getRoot().getChildCount());
			}
		});
		popup.addMenuItem("Delete", new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				System.out.println("Delete Transaction...");
				int row = popup.getRow();
				if (row != -1) {
					TreePath path = table.getPathForRow(row);
					PaymentEntity deleteNode = (PaymentEntity) path.getLastPathComponent();
					model.removeNodeFromParent(deleteNode);
					deleteNode.delete();
				}
			}
		});
	}

	@Override
	public BasicPopupMenu getPopup() {
		return popup;
	}

	@Override
	public void setPopup(BasicPopupMenu newPopup) {
		// TODO Auto-generated method stub

	}

	public class PaymentEntityTableModel extends DefaultTreeTableModel {
		private String[]								columnNamesVerbose	= { "NAME", "ADDRESS", "PHONE" };
		private String[]								columnNames			= { "NAME" };
		private LinkedHashSet<IpaymentEntityListable>	data				= new LinkedHashSet<IpaymentEntityListable>();

		public PaymentEntityTableModel(AbstractDraggableElement root) {
			super((TreeTableNode) root);
			System.out.println("root: " + root);
		}

		public void insertNodeInto(MutableTreeTableNode newChild, MutableTreeTableNode parent, int index) {
			super.insertNodeInto(newChild, parent, Math.min(parent.getChildCount(), index));
			((PaymentEntity) newChild).setParentElement((PaymentEntity) parent);
		}

		public String getColumnName(int col) {
			if (isVerbose) {
				return columnNamesVerbose[col];
			} else {
				return columnNames[col];
			}
		}

		@Override
		public int getColumnCount() {
			if (isVerbose) {
				return columnNamesVerbose.length;
			} else {
				return columnNames.length;
			}
		}

		@Override
		public Object getValueAt(Object node, int col) {
			PaymentEntity entity = (PaymentEntity) node;
			switch (col) {
				case 0:
					return entity.getName();
				case 1:
					return entity.getAddress();
				case 2:
					return entity.getPhoneNumber();
			}
			return null;
		}

		public void setValueAt(Object value, Object node, int column) {
			super.setValueAt(value, node, column);
			PaymentEntity entity = (PaymentEntity) node;
			switch (column) {
				case 0:
					entity.setName((String) value);
					break;
				case 1:
					entity.setAddress(new Address((String) value));
					break;
				case 2:
					entity.setPhoneNumber((String) value);
					break;
			}
		}

		public boolean isCellEditable(Object node, int column) {
			switch (column) {
				case 0:
					return true;
				default:
					return (!((PaymentEntity) node).isGroup());
			}
		}
	}

	public class PaymentEntityTableRenderer extends DefaultTableCellRenderer implements TableCellRenderer {
		public PaymentEntityTableRenderer() {
			super();
		}

		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

			if (value != null) {
				if (value instanceof String) {
					return new JLabel((String) value);
				} else if (value instanceof Address) {
					return new JLabel(((Address) value).toString());
				}
				return new JLabel("Bad Entry");
			} else {
				return new JLabel("Bad Entry");
			}
		}
	}

	public class PaymentEntityTreeRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer {
		public PaymentEntityTreeRenderer() {
			super();
		}

		public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
			if (value != null) {
				if (value instanceof PaymentEntity) {
					return new JLabel(((PaymentEntity) value).getName());
				}
			}
			return new JLabel("Bad Entry");
		}
	}
}
